// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once

#include <gtest/gtest.h>

// gtest v1.10 deprecated *_TEST_CASE in favor of *_TEST_SUITE. These
// macros are provided for portability between different gtest versions.
#ifdef TYPED_TEST_SUITE
#define POLLUX_TYPED_TEST_SUITE TYPED_TEST_SUITE
#else
#define POLLUX_TYPED_TEST_SUITE TYPED_TEST_CASE
#endif

#ifdef INSTANTIATE_TEST_SUITE_P
#define POLLUX_INSTANTIATE_TEST_SUITE_P INSTANTIATE_TEST_SUITE_P
#else
#define POLLUX_INSTANTIATE_TEST_SUITE_P INSTANTIATE_TEST_CASE_P
#endif

// The void static cast supresses the "unused expression result" warning in
// clang.
#define POLLUX_ASSERT_THROW_IMPL(_type, _expression, _errorMessage)    \
  try {                                                               \
    static_cast<void>(_expression);                                   \
    FAIL() << "Expected an exception";                                \
  } catch (const _type& e) {                                          \
    ASSERT_TRUE(e.message().find(_errorMessage) != std::string::npos) \
        << "Expected error message to contain '" << (_errorMessage)   \
        << "', but received '" << e.message() << "'.";                \
  }

#define POLLUX_ASSERT_THROW(_expression, _errorMessage) \
  POLLUX_ASSERT_THROW_IMPL(                             \
      kumo::pollux::PolluxException, _expression, _errorMessage)

#define POLLUX_ASSERT_USER_THROW(_expression, _errorMessage) \
  POLLUX_ASSERT_THROW_IMPL(                                  \
      kumo::pollux::PolluxUserError, _expression, _errorMessage)

#define POLLUX_ASSERT_UNSUPPORTED_THROW(_expression, _errorMessage) \
  POLLUX_ASSERT_THROW_IMPL(                                         \
      kumo::pollux::PolluxUserError, _expression, _errorMessage)

#define POLLUX_ASSERT_RUNTIME_THROW(_expression, _errorMessage) \
  POLLUX_ASSERT_THROW_IMPL(                                     \
      kumo::pollux::PolluxRuntimeError, _expression, _errorMessage)

#define POLLUX_ASSERT_ERROR_STATUS(_expression, _statusCode, _errorMessage) \
  const auto status = (_expression);                                       \
  ASSERT_TRUE(status.code() == _statusCode)                                \
      << "Expected error code to be '" << toString(_statusCode)            \
      << "', but received '" << toString(status.code()) << "'.";           \
  ASSERT_TRUE(status.message().find(_errorMessage) != std::string::npos)   \
      << "Expected error message to contain '" << (_errorMessage)          \
      << "', but received '" << status.message() << "'."

#define POLLUX_ASSERT_ERROR_CODE_IMPL(                                         \
    _type, _expression, _errorCode, _errorMessage)                            \
  try {                                                                       \
    (_expression);                                                            \
    FAIL() << "Expected an exception";                                        \
  } catch (const _type& e) {                                                  \
    ASSERT_TRUE(e.errorCode() == _errorCode)                                  \
        << "Expected error code to be '" << _errorCode << "', but received '" \
        << e.errorCode() << "'.";                                             \
    ASSERT_TRUE(e.message().find(_errorMessage) != std::string::npos)         \
        << "Expected error message to contain '" << (_errorMessage)           \
        << "', but received '" << e.message() << "'.";                        \
  }

#define POLLUX_ASSERT_THROW_CODE(_expression, _errorCode, _errorMessage) \
  POLLUX_ASSERT_ERROR_CODE_IMPL(                                         \
      kumo::pollux::PolluxException, _expression, _errorCode, _errorMessage)

#define POLLUX_ASSERT_USER_THROW_CODE(_expression, _errorCode, _errorMessage) \
  POLLUX_ASSERT_ERROR_CODE_IMPL(                                              \
      kumo::pollux::PolluxUserError, _expression, _errorCode, _errorMessage)

#define POLLUX_ASSERT_RUNTIME_THROW_CODE(    \
    _expression, _errorCode, _errorMessage) \
  POLLUX_ASSERT_ERROR_CODE_IMPL(             \
      kumo::pollux::PolluxRuntimeError,   \
      _expression,                          \
      _errorCode,                           \
      _errorMessage)

#ifndef NDEBUG
#define DEBUG_ONLY_TEST(test_fixture, test_name) TEST(test_fixture, test_name)
#define DEBUG_ONLY_TEST_F(test_fixture, test_name) \
  TEST_F(test_fixture, test_name)
#define DEBUG_ONLY_TEST_P(test_fixture, test_name) \
  TEST_P(test_fixture, test_name)
#else
#define DEBUG_ONLY_TEST(test_fixture, test_name) \
  TEST(test_fixture, DISABLED_##test_name)
#define DEBUG_ONLY_TEST_F(test_fixture, test_name) \
  TEST_F(test_fixture, DISABLED_##test_name)
#define DEBUG_ONLY_TEST_P(test_fixture, test_name) \
  TEST_P(test_fixture, DISABLED_##test_name)
#endif
